#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

int set_sock_keepalive(int sock,int flag,int idle)
{
	if(sock <= 0)
		return -1;

	int keepalive = !!flag;
	int keepinterval = 3;
	int keepcount = 3;

	setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
	setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, (void*)&idle, sizeof(idle));
	setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval));
	setsockopt(sock, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount));
	return 0;
}

int check_sock_valid(int maxsock, fd_set *set)
{
	if(maxsock <= 0 || set == NULL)
		return -1;
	int i = 0;
	int ret = 0;
	char buf[8]={0};
	for(i=1;i<=maxsock;i++)
	{
		if(FD_ISSET(i,set))
		{
			ret = recv(i,buf,1,MSG_DONTWAIT|MSG_PEEK);	
			//if(ret < 0)
			//	printf("!!!!socket[%d] errno = %d: %s\n",i,errno,strerror(errno));
			if(ret < 0 && (errno == EBADF||errno == ETIMEDOUT))
			{
				printf("!!!socket [%d] is bad:[%d]%s\n",i,errno,strerror(errno));
				close(i);
				FD_CLR(i,set);
			}
		}
	}
	return 0;
}

int main(int argc,char *argv[])
{
	int ret = 0;
	int sock = -1;
	struct sockaddr_in serv_addr;
	struct sockaddr_in client_addr;
	socklen_t serv_len,client_len;

	printf(">>>errno value:ETIMEDOUT = %d,EAGAIN = %d,EWOULDBLOCK = %d,EBADF = %d\n",
			ETIMEDOUT,EAGAIN,EWOULDBLOCK,EBADF);
	sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock <= 0)
	{
		printf("socket error:%s\n",strerror(errno));
		exit(EXIT_FAILURE);
	}
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(9001);
	serv_len = sizeof(serv_addr);
	int reuse_addr = 1;
	setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&reuse_addr,sizeof(reuse_addr));
	ret = bind(sock,(struct sockaddr*)&serv_addr,serv_len);
	if(ret < 0)
	{
		printf("bind socket %d error:%s\n",sock,strerror(errno));
		exit(EXIT_FAILURE);
	}
	listen(sock,10);
	client_len = sizeof(client_addr);
	memset(&client_addr,0,client_len);
	int client_sock = 0;
	char recvbuf[100]={0};
	int recv_len = 100;
	time_t pre_time,now_time;
	struct timeval tout;
	int max_sock = sock;
	fd_set myset,tmpset;
	FD_ZERO(&myset);
	FD_SET(sock,&myset);
	FD_ZERO(&tmpset);

	while(1)
	{
		tout.tv_sec = 5;
		tout.tv_usec = 0;
		FD_ZERO(&tmpset);
		tmpset = myset;

		ret = select(max_sock+1,&tmpset,NULL,NULL,&tout);
		if(ret < 0)
		{
			now_time = time(NULL);
			printf("max_sock = %d,now time = %ld\n",max_sock,now_time);
			printf("select error,ret = %d:%s\n",ret,strerror(errno));
			//printf("check sock valid ....\n");
			check_sock_valid(max_sock,&myset);
			//getchar();
		}
		else if(ret == 0)
		{
			printf("select 5 second timeout!\n");
			//printf("check sock valid ....\n");
			check_sock_valid(max_sock,&myset);
		}
		else if(FD_ISSET(sock,&tmpset))
		{
			client_sock = accept(sock,(struct sockaddr*)&client_addr,&client_len);
			if(client_sock < 0)
			{
				printf("accept error:%s\n",strerror(errno));
				continue;
			}
			printf("accept client = %d\n",client_sock);
			pre_time = time(NULL);
			printf("this time = %ld\n",pre_time);
			set_sock_keepalive(client_sock,1,10);
			FD_SET(client_sock,&myset);
			max_sock = (max_sock < client_sock)?client_sock:max_sock;
		}
		else if(FD_ISSET(client_sock,&tmpset))
		{
			ret = recv(client_sock,recvbuf,recv_len,0);	
			if(ret < 0)
			{
				now_time = time(NULL);
				printf("now time = %ld\n",now_time);
				printf("recv [%d] fail:%s[%d]\n",client_sock,strerror(errno),errno);
				close(client_sock);
				FD_CLR(client_sock,&myset);
			}
			else if(ret == 0)
			{
				printf("peer has close socket[%d]\n",client_sock);
				close(client_sock);
				FD_CLR(client_sock,&myset);
			}
			else
			{
				printf("recv [%d] ok,length = %d\n",client_sock,ret);
				//close(client_sock);
			}
		}
	}
	return 0;
}

